﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using BSpec.Conventions;

namespace BSpec
{
    public static class TypeExtensions
    {
        public static Type[] GetTestFixtureTypes(this Assembly assembly)
        {
            var testFixtureTypes = new List<Type>();

            foreach (var type in assembly.GetTypes())
            {
                if (type.IsAbstract == false && TypeHasTestFixtureAttribute(type))
                {
                    testFixtureTypes.Add(type);
                }
            }

            return testFixtureTypes.ToArray();
        }

        public static string GetFriendlyName(this Assembly assembly)
        {
            string assemblyName = assembly.GetName().Name;

            int lastPeriodPosition = assemblyName.LastIndexOf(".");
            if (lastPeriodPosition >= 0)
            {
                assemblyName = assemblyName.Substring(0, lastPeriodPosition);
            }

            return assemblyName;
        }

        private static bool TypeHasTestFixtureAttribute(ICustomAttributeProvider type)
        {
            return type.GetCustomAttributes(true).Any(o => o.GetType().Name.Equals("TestFixtureAttribute"));
        }

        public static MethodInfo[] GetTestMethods(this Type testFixtureType)
        {
            var testMethodTypes = new List<MethodInfo>();

            foreach (var method in testFixtureType.GetMethods(BindingFlags.Public | BindingFlags.Instance))
            {
                if (MethodHasTestAttribute(method))
                {
                    testMethodTypes.Add(method);
                }
            }

            return testMethodTypes.ToArray();
        }

        private static bool MethodHasTestAttribute(ICustomAttributeProvider method)
        {
            return method.GetCustomAttributes(true).Any(o => o.GetType().Name.Equals("TestAttribute"));
        }

        public static string GetName(this Type value, SpecificationElementConvention convention)
        {
            var matchValue = (from pattern in convention.Patterns
                              let match = Regex.Match(value.FullName, pattern, RegexOptions.IgnoreCase)
                              where Regex.Match(value.FullName, pattern, RegexOptions.IgnoreCase).Success
                              select match.Value).DefaultIfEmpty(string.Empty).FirstOrDefault();

            return FormatName(matchValue);
        }

        private static string FormatName(string value)
        {
            return value
                .ReplaceDoubleUnderscoresWithQuotes()
                .ReplaceUnderscoresWithSpace()
                .ReplaceDotWithSpace()
                .ReplaceCamelCaseWithSpace()
                .Trim()
                .CapitaliseFirstLetter();
        }
    }
}